#include <iostream>
#include <fcntl.h>
#include <cstdlib>
#include <stdlib.h>
#include <list>
#include <vector>
#include <cstdio>
#include <fstream>
#include <cstring>
#include <dirent.h>
#include <sys/stat.h>

using namespace std;

const int buffsize = 1024 * 1024;
const char dataDir[] = "/home/leon/trunk/merg/data/";
const char resaultDir[] = "/home/leon/trunk/merg/resault/";

void print(vector<struct file_node *> *);

struct part_node
{
	int send_count;	/* the times file has been send */
	int recv_id;	/* the receiver of this part */
	int send_size;	/* send_size bytes of file have been send */
	int left_size;	/* left_size bytes of file left to be send */
	string dir;		/* the dir contain this part of file */
};

struct file_node
{
	string filename;
	list<struct part_node *> *filelist;	/* list of file parts */

	int filesize;
	int mergsize;						/* mergsize bytes copy to destinate file */
};

bool cmp(struct part_node *node1, struct part_node *node2)
{
	return node1->send_count < node2->send_count;
}

void read_log(vector<struct file_node *> *files, const char *pdir)
{
	cout<<"read_log"<<endl;
	char dir[256];
	strcpy(dir, pdir);
	
	ifstream from((string(dir) + string("/log")).c_str());
	cout<<"open log file: "<<((string(dir) + string("/log")).c_str())<<endl;
	string s;
	if(from == NULL)
	{
		perror("open error");
		exit(1);
	}
	getline(from, s);	/* omit the first line of log file */
	//cout<<"get a line "<<s<<endl;
	while(getline(from, s))
	{
		//cout<<"get a line "<<s<<endl;
		int begine = s.find_first_not_of(' ');	/* first character of part file name */
		int end = s.find_first_of(' ', begine);	/* last character of part file name */
		//cout<<begine<<' '<<end<<endl;

		string filename;
		//filename = s.substr(begine, end - begine);
		end = s.find_last_of('.', end);			/* last character of destinate file name */
		filename = s.substr(begine, end - begine);	/* destinate file name */
		//cout<<begine<<' '<<end<<endl;

		//cout<<"filename: "<<filename<<endl;
		end = s.find_first_of(' ', end);
	
		begine = s.find_first_not_of(' ', end);
		end = s.find_first_of(' ', begine);
		int sendCount = atoi(s.substr(begine, end - begine).c_str());
		//cout<<"send count:"<<sendCount<<endl;

		begine = s.find_first_not_of(' ', end);
		end = s.find_first_of(' ', begine);
		int recvid = atoi(s.substr(begine, end - begine).c_str());
		//cout<<"Receiver ID:"<<recvid<<endl;

		begine = s.find_first_not_of(' ', end);
		end = s.find_first_of(' ', begine);
		int sendsize = atoi(s.substr(begine, end - begine).c_str());
		//cout<<"Send Size:"<<sendsize<<endl;

		begine = s.find_first_not_of(' ', end);
		end = s.find_first_of(' ', begine);
		int leftsize = atoi(s.substr(begine, end - begine).c_str());
		//cout<<"Left Size:"<<leftsize<<endl;
		//cout<<endl;

		int i;
		for(i=0; i<files->size(); i++)
		{
			if(files->at(i)->filename.compare(filename) == 0)
			{
				break;
			}
		}
		if(i == files->size())
		{
			/* insert a new file*/
			struct file_node *filenode = new struct file_node;
			filenode->filename = filename;
			filenode->filelist = new list<struct part_node *>;
			filenode->filesize = (leftsize + sendsize);
			filenode->mergsize = 0;
			files->push_back(filenode);
		}

		/* create a new file part */
		struct part_node *node = new struct part_node;
		node->send_count = sendCount;
		node->recv_id = recvid;
		node->left_size = leftsize;
		node->send_size = sendsize;
		
		/* get the file part directory */
		string	strdir(pdir);
		int lasta = strdir.find_last_of("a");
		strdir.replace(lasta + 1, strdir.length() - lasta -1, "");
		char p[10];
		sprintf(p, "%d", recvid);
		node->dir = strdir + string(p) + "/";
		
		/* insert file part to list */
		files->at(i)->filelist->push_front(node);
	}

	/* sort list by send_count */
	for(int i=0; i<files->size(); i++)
	{
		files->at(i)->filelist->sort(cmp);
	}

	from.close();
}

void print(vector<struct file_node *> *files)
{
	cout<<"vector size: "<<files->size()<<endl;
	for(int i=0; i<files->size(); i++)
	{
		cout<<"filename: "<<files->at(i)->filename<<" filesize: "<<files->at(i)->filesize<<" mergsize: "<<files->at(i)->mergsize<<endl;
		cout<<files->at(i)->filelist->size()<<" parts"<<endl;
		list<struct part_node *> *parts_list = files->at(i)->filelist;
		list<struct part_node *>::iterator j = parts_list->begin();
		int k = 1;
		for(; j != parts_list->end(); j++)
		{
			cout<<"\tpart"<<k<<endl;
			cout<<'\t'<<(*j)->send_count<<endl;
			cout<<'\t'<<(*j)->recv_id<<endl;
			cout<<'\t'<<(*j)->send_size<<endl;
			cout<<'\t'<<(*j)->left_size<<endl;
			cout<<'\t'<<(*j)->dir<<endl;
			cout<<endl;

			k++;
		}
		cout<<endl;
	}
	cout<<endl;
}

void merg(vector<struct file_node *> *files, const char *dir)
{
	mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR);
	cout<<"merg"<<endl;;
	for(int i=0; i<files->size(); i++)
	{
		cout<<"*********************************************"<<endl;
		int out = open((string(dir) + files->at(i)->filename).c_str(), O_WRONLY | O_TRUNC | O_CREAT , S_IRUSR | S_IWUSR);
		cout<<"open "<<string(dir) + files->at(i)->filename<<" for write"<<endl;
		if(out < 0)
		{
			perror("open error");
		}
		list<struct part_node *> *parts_list = files->at(i)->filelist;
		list<struct part_node *>::iterator j = parts_list->begin();
		int k = 0;
		for(; j != parts_list->end(); j++)
		{
			string infilename = files->at(i)->filename;
			if((*j)->send_count > 0)	/* not the first line of a file log */
			{
				if((*j)->left_size > 0)	/* not the last line of a file log */
				{
					char pt[10];
					sprintf(pt, "%d", (*j)->send_count);
					infilename += (".part" + string(pt));
				}
				else					/* the last line of a file log */
				{
					infilename += ".end";
				}

				/*
				cout<<"\tpart"<<k<<endl;
				cout<<"\tinfilename: "<<infilename<<endl;
				cout<<'\t'<<(*j)->send_count<<endl;
				cout<<'\t'<<(*j)->recv_id<<endl;
				cout<<'\t'<<(*j)->send_size<<endl;
				cout<<'\t'<<(*j)->left_size<<endl;
				cout<<"\tsendcount"<<(*j)->send_count<<endl;
				cout<<"\tmergsize"<<files->at(i)->mergsize<<endl;
				*/

				int in = open(((*j)->dir + infilename).c_str(), O_RDONLY);
				cout<<"\topen file "<<(((*j)->dir + infilename).c_str())<<" for read"<<endl;
				if(in <= 0)
				{
					perror("open failed");
				}
				//cout<<"\tpart dir "<<(*j)->dir<<endl;
				char buff[buffsize];
				int partsize = (*j)->send_size - files->at(i)->mergsize;
				//cout<<"\tpartsize: "<<partsize<<endl;
				/* copy a file part to destinate file */
				while(partsize > 0)
				{
					int readcount = read(in, buff, buffsize);
					if(readcount > 0)
					{
						cout<<"\t\treadcount: "<<readcount<<endl;
						partsize -= readcount;
						cout<<"\t\tpartsize: "<<partsize<<endl;
						write(out, buff, readcount);
					}
				}
				/* updata the mergsize */
				files->at(i)->mergsize = (*j)->send_size;
				close(in);
				cout<<"\tclose file "<<(((*j)->dir + infilename).c_str())<<endl;
			}
			k++;
			cout<<"\t++++++++++++++++++++++"<<endl;
		}
		close(out);
		cout<<"close file "<<string(dir) + files->at(i)->filename<<endl;
		cout<<"*********************************************"<<endl;
	}
}
/* search all file log then call read_log*/
void initial(vector<struct file_node *> *files,const char *pdir)
{
	cout<<"initial"<<endl;
	DIR *dp;
	struct dirent *entry;
	struct stat statbuf;
	/* readdir() may change the current directory, so we need save current status */
	char currentDir[256];
	getcwd(currentDir, sizeof(currentDir));

	cout<<"open "<<pdir<<endl;
	if((dp = opendir(pdir)) == NULL)
	{
		perror("open dir error");
	}

	chdir(pdir);
	while((entry = readdir(dp)) != NULL)
	{
		lstat(entry->d_name, &statbuf);
		if(S_ISDIR(statbuf.st_mode))
		{
			/* the directory list may contain "." and ".." */
			if(strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
			{
				continue;
			}
			cout<<entry->d_name<<endl;
			read_log(files, (string(pdir) + string(entry->d_name)).c_str());
			//cout<<"before read log: "<<string(pdir) + string(entry->d_name)<<endl;
		}
		chdir("..");
	}
	
	closedir(dp);
	
	/* resume current directory */
	chdir(currentDir);
}

int main()
{
	vector<struct file_node *> *files = new vector<struct file_node *>;
	initial(files, dataDir);
	print(files);
	merg(files, resaultDir);
}
